package com.joymart.user.service;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.joymart.common.error.handling.JoymartBusinessException;
import com.joymart.common.error.handling.JoymartInternalException;
import com.joymart.common.system.IdGenerator;
import com.joymart.common.utils.CollectionUtils;
import com.joymart.common.utils.DatetimeUtils;
import com.joymart.prepaid.wallet.model.PrepaidWallet;
import com.joymart.prepaid.wallet.service.PrepaidWalletService;
import com.joymart.user.model.BusinessRole;
import com.joymart.user.repo.BusinessRoleRepository;
import com.joymart.wechat.service.WechatOpenPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.joymart.user.model.User;
import com.joymart.user.model.UserPassword;
import com.joymart.user.repo.UserPasswordRepository;
import com.joymart.user.repo.UserRepository;
import org.springframework.transaction.annotation.Transactional;

import java.nio.ByteBuffer;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.zip.CRC32;

@Service
public class UserService {


    @Autowired
    private IdGenerator idGenerator;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private UserPasswordRepository userPasswordRepository;
    @Autowired
    private BusinessRoleRepository businessRoleRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private Algorithm jwtAlgorithm;
    @Autowired
    private JdbcAggregateTemplate jdbcAggregateTemplate;
    @Autowired
    private UserCacheManager userCacheManager;
    @Autowired
    private WechatOpenPlatformService wechatOpenPlatformService;
    @Autowired
    private PrepaidWalletService prepaidWalletService;
    @Autowired
    private UserDetailsService userDetailsService;


    public Optional<User> getDetail(String userId){
        return userRepository.findById(userId);
    }

    /**
     * invitedByCode  ==null 的时候，说明是在创建初始金店铺
     * @param user
     * @param rawPassword
     * @param invitedByCode
     */
    @Transactional
    public void createNonPartyAUser(User user, String rawPassword, String invitedByCode) {

        if(userRepository.existsByUsername(user.getUsername())){
            throw new JoymartBusinessException("该手机号已经被注册");
        }

        assignInvitedByUsingCode(user, invitedByCode);

        assignIdAndInviteCode(user);
        user.setPartyA(false);
        user.setDeleted(false);
        user.setEnabled(true);

        user.setCreatedAt(DatetimeUtils.now());
        jdbcAggregateTemplate.insert(user);

        UserPassword userPassword = new UserPassword();
        userPassword.setUserId(user.getId());
        userPassword.setPassword(passwordEncoder.encode(rawPassword));
        jdbcAggregateTemplate.insert(userPassword);
        if(invitedByCode ==null){ //初始金店
            modifyHighestRole(user, BusinessRole.AU_SHOP);
        }else{   //初始银顾客
            initBusinessRole(user);
            userCacheManager.invalidateUser(user.getInvitedBy());
        }

    }

    private void initBusinessRole(User user) {
        businessRoleRepository.insert(user.getId(), Arrays.asList(BusinessRole.AG_CSR));
        user.getMeta().put("highestRoleLastModifiedAt", DatetimeUtils.now());
        userRepository.save(user);
        userCacheManager.invalidateUser(user.getId());
    }



    @Transactional
    public void createPartyAUser(User user, String rawPassword) {
        if(userRepository.existsByUsername(user.getUsername())){
            throw new JoymartBusinessException("用户名已经存在");
        }
        if(userRepository.existsById(user.getId())){
            throw new JoymartBusinessException("重复ID");
        }

        user.setPartyA(true);
        user.setDeleted(false);
        user.setEnabled(true);
        user.setCreatedAt(DatetimeUtils.now());
        jdbcAggregateTemplate.insert(user);

        UserPassword userPassword = new UserPassword();
        userPassword.setUserId(user.getId());
        userPassword.setPassword(passwordEncoder.encode(rawPassword));
        jdbcAggregateTemplate.insert(userPassword);

    }


    public Map<String,Object> generateJwt(String username , String rawPassword){

        User user = userRepository.findByUsername(username).orElseThrow(()->new JoymartBusinessException("用户不存在"));

        if(!user.isEnabled()){
            throw new JoymartBusinessException("您的帐号已被禁用，请联系管理员");
        }

        UserPassword userPassword = userPasswordRepository.findById(user.getId()).orElseThrow(()->new JoymartBusinessException("未设置初始密码"));
        if(passwordEncoder.matches(rawPassword, userPassword.getPassword())){
            return generateJwt(user);
        }else {
            throw new JoymartBusinessException("密码不正确");
        }
    }

    public Map<String, Object> generateJwt(User user){

        Instant instant = LocalDateTime.now().plusDays(15).toInstant(ZoneOffset.ofHours(+8));

        String token = JWT.create()
                .withIssuer("joymart")
                .withIssuedAt(Instant.now())
                .withExpiresAt(instant)
                .withSubject(user.getId())
                .withClaim("ROLE", user.isPartyA()?"PARTY_A":"NON_PARTY_A")
                .sign(jwtAlgorithm);
        return Map.of("token", token, "expiration", instant.toEpochMilli());
    }


    public void updateUser(User user) {
        userRepository.save(user);
    }


    /**
     * 获取所有用户，包括已经删除了，除了debug不要用。
     * @return
     */
    public List<User> findAll() {
        return CollectionUtils.asList(userRepository.findAll());
    }

    /**
     * 修改用户的最高角色，
     * 删除原有， 插入从银顾客到目标角色的所有角色记录
     */
    @Transactional
    public void modifyHighestRole(User user, BusinessRole businessRole) {

        if(userCacheManager.get(user.getId()).getHighestRole().equals(businessRole)){
            return;
        }

        businessRoleRepository.deleteByUserId(user.getId());

        List<BusinessRole> roles = Arrays.stream(BusinessRole.values())
                .filter(role-> role.compareTo(businessRole)<=0)
                .toList();
        businessRoleRepository.insert(user.getId(),roles);
        user.getMeta().put("highestRoleLastModifiedAt", DatetimeUtils.now());
        userRepository.save(user);
        userCacheManager.invalidateUser(user.getId());

        if(List.of(BusinessRole.AG_CSR, BusinessRole.AU_CSR).contains(businessRole)){
            return ;
        }else{
            PrepaidWallet existing = prepaidWalletService.findByUserId(user.getId());
            if(existing == null){
                PrepaidWallet prepaidWallet = new PrepaidWallet();
                prepaidWallet.setUserId(user.getId());
                prepaidWalletService.create(prepaidWallet);
            }
        }
    }

    /**
     * 给非PartyA用户分配id和邀请码
     * @param user
     */
    private void assignIdAndInviteCode(User user) {
        final String alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
        final int codeLength = 5;
        for(int j =0; j <= 2  ;j ++){ //循环三次，减少出现碰撞的机率
            String id = idGenerator.next();
            CRC32 crc = new CRC32();
            crc.update(ByteBuffer.allocate(Long.BYTES).putLong(Long.parseLong(id)).array());
            long hash = crc.getValue();

            // Step 2: 将哈希值转换为自定义 Base32
            StringBuilder sb = new StringBuilder();
            int base = alphabet.length();

            for (int i = 0; i < codeLength; i++) {
                int index = (int) (hash % base);
                sb.append(alphabet.charAt(index));
                hash /= base;
            }

            String inviteCode = sb.reverse().toString();// 反转结果得到最终的邀请码
            if(!userRepository.existsByInviteCode(inviteCode)){
                user.setId(id);
                user.setInviteCode(inviteCode);
                return;
            }
        }
        throw new JoymartInternalException("服务器忙，请稍后再试");
    }


    /**
     * 如果有邀请人ID则需要验证邀请人存在，且启用未被删除。
     * 如果是null，说明为初始金店。不验证邀请人。
     * 非初始金店在注册的时候在controller层验证过了，所以不必担心
     * @param user
     */
    private void assignInvitedByUsingCode(User user, String inviterCode) {
        if(inviterCode == null){
            return;
        }
        User inviter = userRepository.findByInviteCode(inviterCode);
        if(inviter == null ||inviter.isDeleted()){
            throw new JoymartBusinessException("邀请人不存在，请检查邀请码");
        }
        if(!inviter.isEnabled()){
            throw new JoymartBusinessException("邀请人已被停用");
        }
        user.setInvitedBy(inviter.getId());
    }

    @Transactional
    public void changePassword(String userId, String newPassword) {
        UserPassword userPassword = userPasswordRepository.findById(userId).orElse(null);
        userPassword.setPassword(passwordEncoder.encode(newPassword));
        userPasswordRepository.save(userPassword);
    }

    public void changePassword(String userId, String oldPassword, String newPassword) {
       UserPassword userPassword = userPasswordRepository.findById(userId).orElse(null);
       if(!passwordEncoder.matches(oldPassword, userPassword.getPassword())){
            throw new JoymartBusinessException("原密码不正确");
       }
       changePassword(userId,newPassword);
    }

    public void changeAvatar(String userId, String avatar) {
        User user = userRepository.findById(userId).get();
        user.setMeta(user.getMeta().put("avatar", avatar));
        userRepository.save(user);
        userCacheManager.invalidateUser(userId);
    }

    public void changeQrCode(String userId, String qrCode) {
        User user = userRepository.findById(userId).get();
        user.setMeta(user.getMeta().put("qrCode", qrCode));
        userRepository.save(user);
        userCacheManager.invalidateUser(userId);
    }

    public void bindOpenId(String userId, String code) {
        String openid = wechatOpenPlatformService.jsCode2Session(code).getOpenid();
        User user = userRepository.findById(userId).get();
        user.setOpenId(openid);
        userRepository.save(user);
        userCacheManager.invalidateUser(userId);
    }

    public void changeNickname(String userId, String nickname) {
        User user = userRepository.findById(userId).get();
        user.setNickname(nickname);
        userRepository.save(user);
        userCacheManager.invalidateUser(userId);
    }

    public void changeUsernameAndMobile(String userId, String newMobile) {

        userRepository.findByUsername(newMobile).ifPresent((conflict) -> {throw new JoymartBusinessException("手机号已经被占用");});

        User user = userRepository.findById(userId).get();
        user.setUsername(newMobile);
        user.setMobile(newMobile);
        userRepository.save(user);
        userCacheManager.invalidateUser(userId);
    }

    public void changePaymentText(String userId, String paymentText) {
        User user = userRepository.findById(userId).get();
        user.setMeta(user.getMeta().put("paymentText", paymentText));
        userRepository.save(user);
        userCacheManager.invalidateUser(userId);
    }
}
